home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 101-125 / scopedisk109 / ms-dos / src / hanfile.c < prev    next >
C/C++ Source or Header  |  1995-03-19  |  23KB  |  867 lines

  1. /*-
  2.  * $Id: hanfile.c,v 1.6 90/02/10 21:38:26 Rhialto Exp $
  3.  * $Log:       hanfile.c,v $
  4.  * Revision 1.6  90/02/10  21:38:26  Rhialto
  5.  * Optimized 12-bit fat unpacking.
  6.  * 
  7.  * Revision 1.5  90/01/27  20:26:51  Rhialto
  8.  * Fixed ATTR_ARCHIVED bit in MSWrite()
  9.  *
  10.  * Revision 1.4  90/01/23  02:32:23  Rhialto
  11.  * Add 16-bit FAT support.
  12.  *
  13.  * Revision 1.3  90/01/23  00:39:04  Rhialto
  14.  * Always return -1 on MSWrite error.
  15.  *
  16.  * Revision 1.2  89/12/17  23:04:39  Rhialto
  17.  * Add ATTR_READONLY support
  18.  *
  19.  * Revision 1.1  89/12/17  20:03:11  Rhialto
  20.  * Initial revision
  21.  *
  22.  * HANFILE.C
  23.  *
  24.  * The code for the messydos file system handler.
  25.  *
  26.  * This parts handles files and the File Allocation Table.
  27.  *
  28.  * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved.
  29.  * May not be used or copied without a licence.
  30. -*/
  31.  
  32. #include "han.h"
  33. #include "dos.h"
  34.  
  35. #ifdef DEBUG
  36. #   define     debug(x)  dbprintf x
  37. #else
  38. #   define     debug(x)
  39. #endif
  40.  
  41. extern char    DotDot[1 + 8 + 3];
  42.  
  43. /*
  44.  * Read the FAT from the disk, and count the free clusters.
  45.  */
  46.  
  47. int
  48. GetFat()
  49. {
  50.     int            i;
  51.     byte          *secptr;
  52.  
  53.     if (!Fat && !(Fat = AllocMem((long) Disk.bps * Disk.spf, BufMemType))) {
  54.        debug(("No memory for FAT\n"));
  55.        return 0;
  56.     }
  57.     FatDirty = FALSE;
  58.     for (i = 0; i < Disk.spf; i++) {
  59.        if (secptr = GetSec(Disk.res + i)) {
  60.            CopyMem(secptr, Fat + i * Disk.bps, (long) Disk.bps);
  61.            FreeSec(secptr);
  62.        } else {
  63.            /* q&d way to set the entire FAT to FAT_EOF */
  64.            setmem(Fat + i * Disk.bps, (int) Disk.bps, FAT_EOF);        /* 0xFF */
  65.        }
  66.     }
  67.  
  68.     debug(("counting free clusters\n"));
  69.  
  70.     Disk.nsectsfree = 0;
  71.     for (i = MS_FIRSTCLUST; i <= Disk.maxclst; i++) {
  72.        if (GetFatEntry((word) i) == FAT_UNUSED)
  73.            Disk.nsectsfree += Disk.spc;
  74.     }
  75.  
  76.     return 1;
  77. }
  78.  
  79. void
  80. FreeFat()
  81. {
  82.     if (Fat) {
  83.        FreeMem(Fat, (long) Disk.bps * Disk.spf);
  84.        Fat = NULL;
  85.        FatDirty = FALSE;
  86.     }
  87. }
  88.  
  89. /*-
  90.  *  The FAT consists of 12-bits entries for each cluster,
  91.  *  indicating the next cluster in the chain, or FFF for EOF.
  92.  *
  93.  *  Every two entries are packed in three bytes, like this:
  94.  *
  95.  *  Two entries        abc  123 (for one cluster and the next)
  96.  *  are packed as      bc 3a 12
  97. -*/
  98.  
  99. word
  100. GetFatEntry(cluster)
  101. word           cluster;
  102. {
  103.     if (!Fat && !GetFat())
  104.        return FAT_EOF;
  105.  
  106.     if (Disk.fat16bits) {
  107.        return OtherEndianWord(((word *)Fat)[cluster]);
  108.     } else {
  109.        register int    offset = 3 * (cluster / 2);
  110.        register word   twoentries;
  111.  
  112.        if (cluster & 1) {
  113.            twoentries = Fat[offset + 1] >> 4;
  114.            twoentries |= Fat[offset + 2] << 4;
  115.        } else {
  116.            twoentries = Fat[offset];
  117.            twoentries |= (Fat[offset + 1] & 0x0F) << 8;
  118.        }
  119.  
  120.        /*
  121.         * Convert the special values 0xFF0 .. 0xFFF to 16 bits so they
  122.         * can be checked consistently.
  123.         */
  124.        if (twoentries >= 0xFF0)
  125.            twoentries |= 0xF000;
  126.  
  127.        return twoentries;
  128.     }
  129. }
  130.  
  131. #ifndef READONLY
  132.  
  133. void
  134. SetFatEntry(cluster, value)
  135. word           cluster;
  136. word           value;
  137. {
  138.     if (!Fat && !GetFat())
  139.        return;
  140.  
  141.     if (Disk.fat16bits) {
  142.        ((word *)Fat)[cluster] = OtherEndianWord(value);
  143.     } else {
  144.        register int    offset = 3 * (cluster / 2);
  145.  
  146.        if (cluster & 1) {          /* 123 kind of entry */
  147.            Fat[offset + 2] = value >> 4;
  148.            Fat[offset + 1] &= 0x0F;
  149.            Fat[offset + 1] |= (value & 0x0F) << 4;
  150.        } else {                    /* abc kind of entry */
  151.            Fat[offset + 0] = value;
  152.            Fat[offset + 1] &= 0xF0;
  153.            Fat[offset + 1] |= (value >> 8) & 0x0F;
  154.        }
  155.     }
  156.  
  157.     FatDirty = TRUE;
  158. }
  159.  
  160. /*
  161.  * Find a free cluster to install as the one following this one. Start
  162.  * looking for it right after the given one, so we allocate the cluster
  163.  * chain as contiguous as possible. If we run off the end of the disk, we
  164.  * start again at the beginning. The termination test should not be
  165.  * necessary (and won't work if we are given MSFIRSTCLUST - 1) but won't
  166.  * harm either.
  167.  */
  168.  
  169. word
  170. FindFreeCluster(prev)
  171. word           prev;
  172. {
  173.     register word   i;
  174.  
  175.     if (prev == 0 || prev == FAT_EOF)
  176.        prev = MS_FIRSTCLUST - 1;
  177.  
  178.     if (Disk.nsectsfree >= Disk.spc) {
  179.        for (i = prev + 1; i != prev; i++) {
  180.            if (i > Disk.maxclst)       /* Wrap around */
  181.                i = MS_FIRSTCLUST;
  182.            if (GetFatEntry(i) == FAT_UNUSED) {
  183.                SetFatEntry(i, FAT_EOF);
  184.                if (prev >= MS_FIRSTCLUST)
  185.                    SetFatEntry(prev, i);
  186.                Disk.nsectsfree -= Disk.spc;
  187.                return i;
  188.            }
  189.        }
  190.     }
  191.     return FAT_EOF;
  192. }
  193.  
  194. /*
  195.  * Add a cluster to a cluster chain. For input, we get some cluster we
  196.  * know that is on the chain, even if it is the first one.
  197.  */
  198.  
  199. word
  200. ExtendClusterChain(cluster)
  201. register word  cluster;
  202. {
  203.     register word   nextcluster;
  204.  
  205.     /*
  206.      * Find the end of the cluster chain to tack the new cluster on to.
  207.      * Then FindFreeCluster will (or won't) extend the chain for us.
  208.      */
  209.     if (cluster != 0)
  210.        while ((nextcluster = NextCluster(cluster)) != FAT_EOF) {
  211.            cluster = nextcluster;
  212.        }
  213.  
  214.     return FindFreeCluster(cluster);
  215. }
  216.  
  217. /*
  218.  * Free a chain of clusters by setting their FAT entries to FAT_UNUSED.
  219.  */
  220.  
  221. void
  222. FreeClusterChain(cluster)
  223. register word  cluster;
  224. {
  225.     register word   nextcluster;
  226.  
  227.     while (cluster != FAT_EOF) {
  228.        nextcluster = NextCluster(cluster);
  229.        SetFatEntry(cluster, FAT_UNUSED);
  230.        Disk.nsectsfree += Disk.spc;
  231.        cluster = nextcluster;
  232.     }
  233. }
  234.  
  235. #endif                         /* READONLY */
  236.  
  237. /*
  238.  * This routine opens a file.
  239.  */
  240.  
  241. struct MSFileHandle *
  242. MSOpen(parentdir, name, mode)
  243. struct MSFileLock *parentdir;
  244. char          *name;
  245. long           mode;
  246. {
  247.     struct MSFileLock *fl;
  248.     struct MSFileHandle *fh = NULL;
  249.     long           lockmode;
  250.  
  251.     switch (mode) {
  252.     case MODE_NEWFILE:
  253.     case MODE_READWRITE:
  254.        lockmode = EXCLUSIVE_LOCK ^ MODE_CREATEFILE;
  255.        break;
  256.     default:
  257.        mode = MODE_OLDFILE;
  258.     case MODE_OLDFILE:
  259.        lockmode = SHARED_LOCK;
  260.     }
  261.  
  262.     if (fl = MSLock(parentdir, name, lockmode)) {
  263. makefh:
  264.        if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) {
  265.            error = ERROR_OBJECT_WRONG_TYPE;
  266.            MSUnLock(fl);
  267.        } else if (fh = AllocMem((long) sizeof (*fh), MEMF_PUBLIC)) {
  268. #ifndef READONLY
  269.            /* Do we need to truncate the file? */
  270.            if (mode == MODE_NEWFILE && fl->msfl_Msd.msd_Cluster) {
  271.                FreeClusterChain(fl->msfl_Msd.msd_Cluster);
  272.                fl->msfl_Msd.msd_Cluster = 0;
  273.                fl->msfl_Msd.msd_Filesize = 0;
  274.                UpdateFileLock(fl);
  275.            }
  276. #endif
  277.            fh->msfh_Cluster = fl->msfl_Msd.msd_Cluster;
  278.            fh->msfh_SeekPos = 0;
  279.            fh->msfh_FileLock = fl;
  280.        } else {
  281.            error = ERROR_NO_FREE_STORE;
  282.            MSUnLock(fl);
  283.        }
  284.        return fh;
  285.     }
  286. #ifndef READONLY
  287.     /*
  288.      * If the file was not found, see if we can make a new one. Therefore
  289.      * we need to have an empty spot in the desired directory, and create
  290.      * an MSFileLock for it.
  291.      */
  292.  
  293.     if (!(lockmode & MODE_CREATEFILE) && (fl = EmptyFileLock)) {
  294.        debug(("Creating new file\n"));
  295.        EmptyFileLock = NULL;
  296.        fl->msfl_Msd.msd_Attributes = ATTR_ARCHIVED;
  297.        UpdateFileLock(fl);
  298.  
  299.        goto makefh;
  300.     }
  301.     if (EmptyFileLock) {
  302.        MSUnLock(EmptyFileLock);
  303.        EmptyFileLock = NULL;
  304.     }
  305. #endif
  306.  
  307.     return NULL;
  308. }
  309.  
  310. void
  311. MSClose(fh)
  312. register struct MSFileHandle *fh;
  313. {
  314.     if (fh) {
  315.        MSUnLock(fh->msfh_FileLock);
  316.        FreeMem(fh, (long) sizeof (*fh));
  317.     }
  318. }
  319.  
  320. long
  321. MSSeek(fh, position, mode)
  322. struct MSFileHandle *fh;
  323. long           position;
  324. long           mode;
  325. {
  326.     long           oldpos = fh->msfh_SeekPos;
  327.     long           newpos = oldpos;
  328.     long           filesize = fh->msfh_FileLock->msfl_Msd.msd_Filesize;
  329.     word           cluster = fh->msfh_Cluster;
  330.     word           oldcluster;
  331.     word           newcluster;
  332.  
  333.     switch (mode) {
  334.     case OFFSET_BEGINNING:
  335.        newpos = position;
  336.        break;
  337.     case OFFSET_CURRENT:
  338.        newpos += position;
  339.        break;
  340.     case OFFSET_END:
  341.        newpos = filesize - position;
  342.        break;
  343.     }
  344.  
  345.     if (newpos < 0 || newpos > filesize) {
  346.        error = ERROR_SEEK_ERROR;
  347.        return -1;
  348.     }
  349.     newcluster = newpos / Disk.bpc;
  350.     oldcluster = oldpos / Disk.bpc;
  351.  
  352.     if (oldcluster > newcluster) {      /* Seek backwards */
  353.        cluster = fh->msfh_FileLock->msfl_Msd.msd_Cluster;
  354.        oldcluster = 0;
  355.     }
  356.     if (oldcluster < newcluster) {
  357.        if (CheckLock(fh->msfh_FileLock))
  358.            return -1L;
  359.        while (oldcluster < newcluster) {
  360.            cluster = NextCluster(cluster);
  361.            oldcluster++;
  362.        }
  363.     }
  364.     fh->msfh_Cluster = cluster;
  365.     fh->msfh_SeekPos = newpos;
  366.  
  367.     return oldpos;
  368. }
  369.  
  370. long
  371. MSRead(fh, userbuffer, size)
  372. register struct MSFileHandle *fh;
  373. register byte  *userbuffer;
  374. register long  size;
  375. {
  376.     long           oldsize;
  377.  
  378.     if (CheckLock(fh->msfh_FileLock))
  379.        return -1L;
  380.  
  381.     if (fh->msfh_SeekPos + size > fh->msfh_FileLock->msfl_Msd.msd_Filesize)
  382.        size = fh->msfh_FileLock->msfl_Msd.msd_Filesize - fh->msfh_SeekPos;
  383.  
  384.     oldsize = size;
  385.  
  386.     while (size > 0) {
  387.        word            offset;
  388.        word            sector;
  389.        byte           *diskbuffer;
  390.        long            insector;
  391.        long            tocopy;
  392.  
  393.        offset = fh->msfh_SeekPos % Disk.bpc;
  394.        sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
  395.        if (diskbuffer = GetSec(sector)) {
  396.            offset %= Disk.bps;
  397.            insector = Disk.bps - offset;
  398.            tocopy = lmin(size, insector);
  399.  
  400.            CopyMem(diskbuffer + offset, userbuffer, tocopy);
  401.            userbuffer += tocopy;
  402.            size -= tocopy;
  403.            FreeSec(diskbuffer);
  404.            /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
  405.            if ((fh->msfh_SeekPos += tocopy) % Disk.bpc == 0)
  406.                fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
  407.        } else {                /* Read error. Return amount successfully
  408.                                 * read, if any. Else return -1 for error. */
  409.            if (size == oldsize) {
  410.                return -1L;
  411.            }
  412.            return oldsize - size;
  413.        }
  414.     }
  415.  
  416.     return oldsize;
  417. }
  418.  
  419. long
  420. MSWrite(fh, userbuffer, size)
  421. register struct MSFileHandle *fh;
  422. register byte  *userbuffer;
  423. register long  size;
  424. {
  425. #ifdef READONLY
  426.     return -1;
  427. #else
  428.     long           oldsize;
  429.     struct MSFileLock *fl = fh->msfh_FileLock;
  430.     word           prevclust = fl->msfl_Msd.msd_Cluster;
  431.     word           update = 0;
  432.  
  433.     if (CheckLock(fl))
  434.        return -1;
  435.  
  436.     if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
  437.        error = ERROR_WRITE_PROTECTED;
  438.        return -1;
  439.     }
  440.  
  441.     oldsize = size;
  442.  
  443.     while (size > 0) {
  444.        /*
  445.         * Do we need to extend the file?
  446.         */
  447.  
  448.        if (fh->msfh_Cluster == 0 || fh->msfh_Cluster == FAT_EOF) {
  449.            word            newclust;
  450.  
  451.            newclust = ExtendClusterChain(prevclust);
  452.            debug(("Extend with %d\n", newclust));
  453.            if (newclust != FAT_EOF) {
  454.                if (prevclust == 0) {   /* Record first cluster in dir */
  455.                    fl->msfl_Msd.msd_Cluster = newclust;
  456.                }
  457.                fh->msfh_Cluster = newclust;
  458.                prevclust = newclust;
  459.            } else {
  460.                error = ERROR_DISK_FULL;
  461.                goto error;
  462.            }
  463.        }
  464.        {
  465.            word            offset;
  466.            word            sector;
  467.            byte           *diskbuffer;
  468.            long            insector;
  469.            long            tocopy;
  470.  
  471.            offset = fh->msfh_SeekPos % Disk.bpc;
  472.            sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
  473.            offset %= Disk.bps;
  474.            insector = Disk.bps - offset;
  475.            tocopy = lmin(size, insector);
  476.  
  477.            if (tocopy == Disk.bps)
  478.                diskbuffer = EmptySec(sector);
  479.            else
  480.                diskbuffer = GetSec(sector);
  481.  
  482.            if (diskbuffer != NULL) {
  483.                CopyMem(userbuffer, diskbuffer + offset, tocopy);
  484.                userbuffer += tocopy;
  485.                size -= tocopy;
  486.                MarkSecDirty(diskbuffer);
  487.                FreeSec(diskbuffer);
  488.                /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
  489.                if ((fh->msfh_SeekPos += tocopy) % Disk.bpc == 0)
  490.                    fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
  491.                if (fh->msfh_SeekPos > fl->msfl_Msd.msd_Filesize)
  492.                    fl->msfl_Msd.msd_Filesize = fh->msfh_SeekPos;
  493.                fl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED;
  494.                update = 1;
  495.            } else {            /* Write error. */
  496.        error:
  497.                if (update)
  498.                    UpdateFileLock(fl);
  499. #if 1
  500.                return -1;      /* We loose the information about how much
  501.                                 * data we wrote, but the standard file system
  502.                                 * seems to do it this way. */
  503. #else
  504.                if (size == oldsize) {
  505.                    return -1;
  506.                }
  507.                return oldsize - size;  /* Amount successfully written */
  508. #endif
  509.            }
  510.        }
  511.     }
  512.  
  513.     if (update)
  514.        UpdateFileLock(fl);
  515.  
  516.     return oldsize;
  517. #endif
  518. }
  519.  
  520. long
  521. MSDeleteFile(parentdir, name)
  522. struct MSFileLock *parentdir;
  523. byte          *name;
  524. {
  525. #ifdef READONLY
  526.     return DOSFALSE;
  527. #else
  528.     register struct MSFileLock *fl;
  529.  
  530.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
  531.     if (fl) {
  532.        if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
  533.            error = ERROR_DELETE_PROTECTED;
  534.            goto error;
  535.        }
  536.        if (fl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY) {
  537.            struct FileInfoBlock fib;
  538.  
  539.            /*
  540.             * We normally can't get REAL exclusive locks on directories,
  541.             * so we check here just to be sure. We don't want to delete
  542.             * anyone's current directory, do we?
  543.             */
  544.  
  545.            if (fl->msfl_Refcount != 1 || fl == RootLock) {
  546.                error = ERROR_OBJECT_IN_USE;
  547.                goto error;
  548.            }
  549.            if (MSExamine(fl, &fib) &&  /* directory itself */
  550.                MSExNext(fl, &fib)) {   /* should fail */
  551.                if (error == 0) {
  552.            not_empty:
  553.                    error = ERROR_DIRECTORY_NOT_EMPTY;
  554.            error:
  555.                    MSUnLock(fl);
  556.                    return DOSFALSE;
  557.                }
  558.            }
  559.            if (error != ERROR_NO_MORE_ENTRIES)
  560.                goto error;
  561.  
  562.            error = 0;
  563.        }
  564.        if (fl->msfl_Msd.msd_Cluster)
  565.            FreeClusterChain(fl->msfl_Msd.msd_Cluster);
  566.        fl->msfl_Msd.msd_Name[0] = DIR_DELETED;
  567.        WriteFileLock(fl);
  568.        MSUnLock(fl);
  569.  
  570.        return DOSTRUE;
  571.     }
  572.     return DOSFALSE;
  573. #endif
  574. }
  575.  
  576. long
  577. MSSetDate(parentdir, name, datestamp)
  578. struct MSFileLock *parentdir;
  579. byte          *name;
  580. struct DateStamp *datestamp;
  581. {
  582. #ifdef READONLY
  583.     return DOSFALSE;
  584. #else
  585.     register struct MSFileLock *fl;
  586.  
  587.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
  588.     if (fl) {
  589.        ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, datestamp);
  590.        WriteFileLock(fl);
  591.        MSUnLock(fl);
  592.  
  593.        return DOSTRUE;
  594.     }
  595.     return DOSFALSE;
  596. #endif
  597. }
  598.  
  599. /*
  600.  * Create a new directory, with its own initial "." and ".." entries.
  601.  */
  602.  
  603. struct MSFileLock *
  604. MSCreateDir(parentdir, name)
  605. struct MSFileLock *parentdir;
  606. byte          *name;
  607. {
  608. #ifdef READONLY
  609.     return DOSFALSE;
  610. #else
  611.     register struct MSFileLock *fl;
  612.  
  613.     /*
  614.      * Go create a new file. If we fail later, we have an empty file that
  615.      * we delete again.
  616.      */
  617.  
  618.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
  619.     if (fl || error == ERROR_OBJECT_IN_USE) {
  620.        error = ERROR_OBJECT_EXISTS;
  621.        goto error;
  622.     }
  623.     if (error != 0) {
  624.        goto error;
  625.     }
  626.     if (fl = EmptyFileLock) {
  627.        debug(("Creating new dir\n"));
  628.        EmptyFileLock = NULL;
  629.        if ((fl->msfl_Msd.msd_Cluster = FindFreeCluster(FAT_EOF)) != FAT_EOF) {
  630.            struct MsDirEntry direntry;
  631.            byte           *sec;
  632.            word            sector;
  633.  
  634.            sector = ClusterToSector(fl->msfl_Msd.msd_Cluster);
  635.            sec = EmptySec(sector);
  636.            if (sec == NULL)
  637.                goto error_no_free_store;
  638.            setmem(sec, (int) Disk.bps, 0);
  639.  
  640.            /*
  641.             * Turn the file into a directory.
  642.             */
  643.            fl->msfl_Msd.msd_Attributes = ATTR_DIRECTORY;
  644.            UpdateFileLock(fl);
  645.  
  646.            /*
  647.             * Create the "." entry.
  648.             */
  649.            direntry = fl->msfl_Msd;
  650.            strncpy(direntry.msd_Name, DotDot + 1, 8 + 3);
  651.            OtherEndianMsd(&direntry);
  652.            ((struct MsDirEntry *) sec)[0] = direntry;
  653.  
  654.            /*
  655.             * Get the real parent directory because we will duplicate the
  656.             * directory entry in the subdirectory.
  657.             */
  658.  
  659.            parentdir = MSParentDir(fl);
  660.            if (parentdir == NULL)      /* Cannot happen */
  661.                parentdir = MSDupLock(RootLock);
  662.  
  663.            /*
  664.             * Create the ".." entry.
  665.             */
  666.            direntry = parentdir->msfl_Msd;
  667.            strncpy(direntry.msd_Name, DotDot, 8 + 3);
  668.            direntry.msd_Attributes = ATTR_DIRECTORY;
  669.            OtherEndianMsd(&direntry);
  670.            ((struct MsDirEntry *) sec)[1] = direntry;
  671.  
  672.            MSUnLock(parentdir);
  673.  
  674.            MarkSecDirty(sec);
  675.            FreeSec(sec);
  676.  
  677.            /*
  678.             * Clear out the rest of the newly created directory.
  679.             */
  680.  
  681.            while ((sector = NextClusteredSector(sector)) != SEC_EOF) {
  682.                sec = EmptySec(sector);
  683.                if (sec == NULL)
  684.                    goto error_no_free_store;
  685.                setmem(sec, (int) Disk.bps, 0);
  686.                MarkSecDirty(sec);
  687.                FreeSec(sec);
  688.            }
  689.        } else {
  690.            MSUnLock(fl);
  691.            fl = NULL;
  692.            MSDeleteFile(parentdir, name);
  693.            error = ERROR_DISK_FULL;
  694.        }
  695.     }
  696.     if (EmptyFileLock) {
  697.        MSUnLock(EmptyFileLock);
  698.        EmptyFileLock = NULL;
  699.     }
  700.     return fl;
  701.  
  702. error_no_free_store:
  703.     error = ERROR_NO_FREE_STORE;
  704. error:
  705.     if (fl)
  706.        MSUnLock(fl);
  707.     return DOSFALSE;
  708. #endif
  709. }
  710.  
  711. /*
  712.  * Rename a file or directory, possibly moving it to a different
  713.  * directory.
  714.  *
  715.  * "Tuned" to also work in full directories by first deleting the source
  716.  * name, then look for a slot to put the destination name. If that fails,
  717.  * we undo the deletion. By playing with the cache, we even avoid a write
  718.  * of the sector with the undeleted entry.
  719.  */
  720.  
  721. long
  722. MSRename(slock, sname, dlock, dname)
  723. struct MSFileLock *slock;
  724. byte          *sname;
  725. struct MSFileLock *dlock;
  726. byte          *dname;
  727. {
  728. #ifdef READONLY
  729.     return DOSFALSE;
  730. #else
  731.     struct MSFileLock *sfl;
  732.     struct MSFileLock *dfl;
  733.     long           success;
  734.     struct CacheSec *scache;
  735.     ulong          oldstatus;
  736.  
  737.     success = DOSFALSE;
  738.     scache = NULL;
  739.     dfl = NULL;
  740.  
  741.     sfl = MSLock(slock, sname, SHARED_LOCK);
  742.     if (sfl == NULL || sfl == RootLock)
  743.        goto error;
  744.  
  745.     /*
  746.      * Now we are going to pull a dirty trick with the cache. We are going
  747.      * to temporarily delete the source file, in the chache only, and
  748.      * undelete it again if we cannot create the new name. And above all
  749.      * we want to avoid unnecessary writes if we decide not to do the
  750.      * deletion after all.
  751.      */
  752.     {
  753.        byte           *sec;
  754.        byte            old;
  755.  
  756.        if ((sec = GetSec(sfl->msfl_DirSector)) == NULL)
  757.            goto error;
  758.        scache = FindSecByBuffer(sec);
  759.        oldstatus = scache->sec_Refcount;
  760.  
  761.        old = sfl->msfl_Msd.msd_Name[0];
  762.        sfl->msfl_Msd.msd_Name[0] = DIR_DELETED;
  763.        WriteFileLock(sfl);
  764.        sfl->msfl_Msd.msd_Name[0] = old;
  765.  
  766.        /*
  767.         * Don't FreeSec it yet; we don't want it written out to disk.
  768.         */
  769.     }
  770.  
  771.     /*
  772.      * Now we have freed the directory entry of the source name, we might
  773.      * be able to use it for the destination name. But only if we also
  774.      * temporarily hide the MSFileLock on that spot. Gross hack ahead!
  775.      */
  776.  
  777.     sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
  778.     dfl = MSLock(dlock, dname, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
  779.     sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
  780.  
  781.     if (dfl != NULL || error == ERROR_OBJECT_IN_USE) {
  782.        error = ERROR_OBJECT_EXISTS;
  783.        goto undelete;
  784.     }
  785.     dfl = EmptyFileLock;
  786.     EmptyFileLock = NULL;
  787.     if (dfl == NULL) {
  788.        /*
  789.         * Sigh, we could not create the new name. But because of that, we
  790.         * are sure that we need to write nothing to the disk at all. So
  791.         * we can safely reset the sector-dirty flag to what it was
  792.         * before, if we also restore the cached sector.
  793.         */
  794. undelete:
  795.        WriteFileLock(sfl);
  796.        scache->sec_Refcount = oldstatus;
  797.        goto error;
  798.     }
  799.     /*
  800.      * Now, if the moved entry was a directory, and it was moved to a
  801.      * different directory, we need to adapt its "..", which is the second
  802.      * entry.
  803.      */
  804.  
  805.     if (sfl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY &&
  806.        sfl->msfl_Parent != dfl->msfl_Parent) {
  807.        struct MSFileLock *parentdir;
  808.        struct MsDirEntry *dir;
  809.  
  810.        if (dir = (struct MsDirEntry *)
  811.            GetSec(DirClusterToSector(sfl->msfl_Msd.msd_Cluster))) {
  812.            parentdir = MSParentDir(dfl);
  813.            /*
  814.             * Copy everything except the name which must remain "..". But
  815.             * first a quick consistency check...
  816.             */
  817.            debug(("Creating new \"..\"  "));
  818.            if (dir[1].msd_Name[1] == '.') {
  819.                CopyMem(&parentdir->msfl_Msd.msd_Attributes,
  820.                        &dir[1].msd_Attributes,
  821.                        (long) sizeof (struct MsDirEntry) -
  822.                        OFFSETOF(MsDirEntry, msd_Attributes));
  823.                dir[1].msd_Attributes = ATTR_DIRECTORY;
  824.                OtherEndianMsd(&dir[1]);
  825.                MarkSecDirty(dir);
  826.            }
  827. #ifdef DEBUG
  828.            else
  829.                debug(("!!! No \"..\" found ??\n"));
  830. #endif
  831.            MSUnLock(parentdir);
  832.            FreeSec(dir);
  833.        }
  834.     }
  835.     /*
  836.      * Move the name from the new entry to the old filelock. We do this
  837.      * for the case that somebody else has a lock on the (possibly moved)
  838.      * file/directory. Also move the other administration.
  839.      */
  840.  
  841.     strncpy(sfl->msfl_Msd.msd_Name, dfl->msfl_Msd.msd_Name, 8 + 3);
  842.     sfl->msfl_DirSector = dfl->msfl_DirSector;
  843.     sfl->msfl_DirOffset = dfl->msfl_DirOffset;
  844.     /*
  845.      * Free the old, and get the new parent directory. They might be the
  846.      * same, of course...
  847.      */
  848.     MSUnLock(sfl->msfl_Parent);
  849.     sfl->msfl_Parent = dfl->msfl_Parent;
  850.     dfl->msfl_Parent = NULL;
  851.     sfl->msfl_Msd.msd_Attributes &= ~ATTR_ARCHIVED;
  852.     WriteFileLock(sfl);         /* Write the new name; the old name
  853.                                 * already has been deleted. */
  854.     success = DOSTRUE;
  855.  
  856. error:
  857.     if (sfl)
  858.        MSUnLock(sfl);
  859.     if (dfl)
  860.        MSUnLock(dfl);
  861.     if (scache)
  862.        FreeSec(scache->sec_Data);
  863.  
  864.     return success;
  865. #endif
  866. }
  867.